package core

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"math/rand"
	"sync"
	"zinx/mmo_game_zinx/pb"
	"zinx/ziface"
)

type Player struct {
	Pid  int32
	Conn ziface.IConnection
	X    float32 //玩家x坐标
	Y    float32 //玩家高度
	Z    float32 //玩家Y坐标
	V    float32 //玩家角度
}

var Pid int32 = 0
var PidLock sync.Mutex

func NewPlayer(conn ziface.IConnection) *Player {
	//生成玩家ID
	PidLock.Lock()
	Pid++
	PidLock.Unlock()
	//创建一个玩家对象
	p := &Player{
		Pid:  Pid,
		Conn: conn,
		X:    float32(160 + rand.Intn(10)),
		Y:    0,
		Z:    float32(140 + rand.Intn(20)),
		V:    0,
	}
	return p
}

// SendMsg 提供一个给客户端发送消息的方法 主要是为了先将pb对应的protobuf数据序列化后交由客户端，再调用zinx的SendMsg方法
func (p *Player) SendMsg(msgId uint32, data proto.Message) {
	//序列化
	msg, err := proto.Marshal(data)
	if err != nil {
		fmt.Println("Marshal msg err!")
		return
	}
	//将二进制文件通过zinx的SendMsg方法发送给客户端
	if p.Conn == nil {
		fmt.Println("conn is nil!")
		return
	}
	if err := p.Conn.SendMsg(msgId, msg); err != nil {
		fmt.Println("Player SendMsg err!")
		return
	}
	return
}

// SyncPid 将playerID同步给客户端
func (p *Player) SyncPid() {
	//组建Proto数据
	protoMsg := &pb.SyncPid{
		Pid: p.Pid,
	}
	//将数据发送给客户端
	p.SendMsg(1, protoMsg)
}

// BroadCastStartPosition 广播玩家position
func (p *Player) BroadCastStartPosition() {
	//组建Proto数据
	protoMsg := &pb.BroadCast{
		Pid: p.Pid,
		Tp:  2,
		Data: &pb.BroadCast_P{
			P: &pb.Position{
				X: p.X,
				Y: p.Y,
				Z: p.Z,
				V: p.V,
			},
		},
	}
	//发送数据给客户端
	p.SendMsg(200, protoMsg)
}

// Talk 玩家广播世界聊天消息
func (p *Player) Talk(content string) {
	//组建MsgID为200的proto数据
	protoMsg := &pb.BroadCast{
		Pid: p.Pid,
		Tp:  1,
		Data: &pb.BroadCast_Content{
			Content: content,
		},
	}
	//得到当前世界在线的所有玩家
	players := WorldManagerObj.GetAllPlayers()
	//向所有玩家发送MsgID为200的消息
	for _, p := range players {
		p.SendMsg(200, protoMsg)
	}
}

// SyncSurrounding 同步玩家上线的位置信息
func (p *Player) SyncSurrounding() {
	//获取当前玩家周围的玩家集合
	pids := WorldManagerObj.AoiMgr.GetPidsByPos(p.X, p.Z)
	players := make([]*Player, 0, len(pids))
	for _, pid := range pids {
		players = append(players, WorldManagerObj.GetPlayerByPid(int32(pid)))
	}
	//组建MsgID为200的数据(让其他玩家看到自己)
	proto_msg := &pb.BroadCast{
		Pid: p.Pid,
		Tp:  2,
		Data: &pb.BroadCast_P{
			P: &pb.Position{
				X: p.X,
				Y: p.Y,
				Z: p.Z,
				V: p.V,
			},
		},
	}
	//向当前玩家附近所有的玩家客户端发送200消息
	for _, player := range players {
		player.SendMsg(200, proto_msg)
	}
	//组建MsgID=202的数据，让自己看到其他人
	playersProtoMsg := make([]*pb.Player, 0, len(players))
	for _, player := range players {
		p := &pb.Player{
			Pid: player.Pid,
			P: &pb.Position{
				X: player.X,
				Y: player.Y,
				Z: player.Z,
				V: player.V,
			},
		}
		playersProtoMsg = append(playersProtoMsg, p)
	}
	syncPlayersProtoMsg := &pb.SyncPlayers{
		Ps: playersProtoMsg[:],
	}
	//将周围全部玩家的位置信息发送给当前玩家
	p.SendMsg(202, syncPlayersProtoMsg)
}

// UpdatePos 广播当前玩家的位置信息
func (p *Player) UpdatePos(x float32, y float32, z float32, v float32) {
	//先更新当前玩家自己的坐标
	p.X, p.Y, p.Z, p.V = x, y, z, v
	//再将当前玩家的坐标广播给其他玩家
	protoMsg := &pb.BroadCast{
		Pid: p.Pid,
		Tp:  4,
		Data: &pb.BroadCast_P{
			P: &pb.Position{
				X: p.X,
				Y: p.Y,
				Z: p.Z,
				V: p.V,
			},
		},
	}
	//获取当前玩家周边九宫格内的所有玩家信息
	players := p.GetSurroundingPlayers()
	//广播消息
	for _, player := range players {
		player.SendMsg(200, protoMsg)
	}
}

func (p *Player) GetSurroundingPlayers() []*Player {
	//得到所有Pid
	pids := WorldManagerObj.AoiMgr.GetPidsByPos(p.X, p.Z)
	players := make([]*Player, 0, len(pids))
	for _, pid := range pids {
		players = append(players, WorldManagerObj.GetPlayerByPid(int32(pid)))
	}
	return players
}

// OffLine 玩家下线
func (p *Player) OffLine() {
	//获取周围玩家
	players := p.GetSurroundingPlayers()
	//给周围玩家广播MsgID为201的消息
	protoMsg := &pb.SyncPid{
		Pid: p.Pid,
	}
	for _, player := range players {
		player.SendMsg(201, protoMsg)
	}
	WorldManagerObj.AoiMgr.RemoveFromGridByPos(int(p.Pid), p.X, p.Z)
	WorldManagerObj.RemovePlayerByPid(p.Pid)
}
